{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [Java中的static关键字解析](https://www.cnblogs.com/dolphin0520/p/3799052.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## static关键字的用途\n",
    "在《Java编程思想》P86页有这样一段话：\n",
    "\n",
    "　　**“static方法就是没有this的方法。在static方法内部不能调用非静态方法，反过来是可以的。而且可以在没有创建任何对象的前提下，仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”**\n",
    "  \n",
    "这段话虽然只是说明了static方法的特殊之处，但是可以看出static关键字的基本作用，简而言之，一句话来描述就是：\n",
    "   \n",
    "**方便在没有创建对象的情况下来进行调用（方法/变量）。**\n",
    "\n",
    "很显然，被static关键字修饰的方法或者变量不需要依赖于对象来进行访问，只要类被加载了，就可以通过类名去进行访问。\n",
    "\n",
    "static可以用来`修饰类的成员方法、类的成员变量，另外可以编写static代码块来优化程序性能。`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### static方法\n",
    "static方法一般称作静态方法，由于静态方法不依赖于任何对象就可以进行访问，因此对于静态方法来说，是没有this的，因为它不依附于任何对象，既然都没有对象，就谈不上this了。并且由于这个特性，在静态方法中不能访问类的非静态成员变量和非静态成员方法，因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。\n",
    "\n",
    "但是要注意的是，虽然在静态方法中不能访问非静态成员方法和非静态成员变量，但是在非静态成员方法中是可以访问静态成员方法/变量的。举个简单的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "class MyObject{\n",
    "    private static String str1 = \"staticProperty\";\n",
    "    private String str2 = \"property\";\n",
    "    \n",
    "    public MyObject(){}\n",
    "    \n",
    "    public void print1(){\n",
    "        System.out.println(str1);\n",
    "        System.out.println(str2);\n",
    "    }\n",
    "    \n",
    "    public static void print2(){\n",
    "        System.out.println(str1);\n",
    "        //无对象，str2 & print1()不存在\n",
    "        //System.out.println(str2);\n",
    "        //print1();\n",
    "    }\n",
    "}\n",
    "\n",
    "//由于print2方法是独立于对象存在的，可以直接用过类名调用\n",
    "MyObject.print2();\n",
    "\n",
    "MyObject myObject = new MyObject();\n",
    "myObject.print1();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上面的代码中，由于print2方法是独立于对象存在的，可以直接用过类名调用。假如说可以在静态方法中访问非静态方法/变量的话，那么如果在main方法中有下面一条语句：\n",
    "\n",
    "　　`MyObject.print2();`\n",
    "\n",
    "　　此时对象都没有，str2根本就不存在，所以就会产生矛盾了。同样对于方法也是一样，由于你无法预知在print1方法中是否访问了非静态成员变量，所以也禁止在静态成员方法中访问非静态成员方法。\n",
    "\n",
    "　　而对于非静态成员方法，它访问静态成员方法/变量显然是毫无限制的。\n",
    "\n",
    "　　因此，如果说想在不创建对象的情况下调用某个方法，就可以将这个方法设置为static。我们最常见的static方法就是main方法，至于为什么main方法必须是static的，现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象，因此只有通过类名来访问。\n",
    "\n",
    "#### 另外记住，关于构造器是否是static方法可参考：http://blog.csdn.net/qq_17864929/article/details/48006835"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "code_folding": []
   },
   "outputs": [],
   "source": [
    "//import java.util.Scanner;\n",
    "//import java.util.Random;\n",
    "\n",
    "class Add{\n",
    "    private int add;\n",
    "    private static int a,b;\n",
    "     \n",
    "    static int sum(int n, int m) {\n",
    "        Random ran = new Random();\n",
    "        a = ran.nextInt(n);\n",
    "        b = ran.nextInt(m);\n",
    "        return a+b;\n",
    "    }\n",
    "}\n",
    "\n",
    "Scanner scanner = new Scanner(System.in);\n",
    "int n = scanner.nextInt();\n",
    "int m = scanner.nextInt();\n",
    "\n",
    "Add.sum(n,m);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### static变量\n",
    "　　static变量也称作静态变量，静态变量和非静态变量的区别是：静态变量被所有的对象所共享，在内存中只有一个副本，它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的，在创建对象的时候被初始化，存在多个副本，各个对象拥有的副本互不影响。\n",
    "\n",
    "　　static成员变量的初始化顺序按照定义的顺序进行初始化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### static代码块\n",
    "　　static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方，类中可以有多个static块。在类初次被加载的时候，会按照static块的顺序来执行每个static块，并且只会执行一次。\n",
    "\n",
    "　　为什么说static块可以用来优化程序性能，是因为它的特性:只会在类加载的时候执行一次。下面看个例子:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person{\n",
    "    private Date birthDate;\n",
    "    private static Date startDate,endDate;\n",
    "    static{\n",
    "        startDate = Date.valueOf(\"1946\");\n",
    "        endDate = Date.valueOf(\"1964\");\n",
    "    }\n",
    "     \n",
    "    public Person(Date birthDate) {\n",
    "        this.birthDate = birthDate;\n",
    "    }\n",
    "     \n",
    "    boolean isBornBoomer() {\n",
    "        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## static关键字的误区\n",
    "### static关键字不会改变类中成员的访问权限\n",
    "\n",
    "　　有些初学的朋友会将java中的static与C/C++中的static关键字的功能混淆了。在这里只需要记住一点：与C/C++中的static不同，Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected（包括包访问权限）这几个关键字。看下面的例子就明白了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person{\n",
    "    public static String name = \"anlzou\";\n",
    "    private static int age = 18;\n",
    "}\n",
    "\n",
    "System.out.println(Person.name);\n",
    "\n",
    "//private access in Person\n",
    "//System.out.println(Person.age);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 通过this访问静态成员变量(是否创建对象)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class Main {\n",
    "    static int value = 33;\n",
    "\n",
    "    public void printValue(){\n",
    "        int value = 3;\n",
    "        System.out.println(this.value);\n",
    "    }\n",
    "}\n",
    "\n",
    "// 创建对象\n",
    "new Main().printValue();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class Main {\n",
    "    static int value = 33;\n",
    "\n",
    "    public static void printValue(){\n",
    "        int value = 3;\n",
    "        System.out.println(value);\n",
    "    }\n",
    "}\n",
    "\n",
    "// 不创建对象\n",
    "Main.printValue();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在C/C++中static是可以作用域局部变量的，但是在Java中切记：static是不允许用来修饰局部变量。不要问为什么，这是Java语法的规定。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见的笔试面试题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **先加载静态代码块，然后加载构造器**\n",
    "- **存在继承关系，先调用父类构造器**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 下面这段代码的输出结果是什么？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Base{\n",
    "     \n",
    "    static{\n",
    "        System.out.println(\"base static\");\n",
    "    }\n",
    "     \n",
    "    public Base(){\n",
    "        System.out.println(\"base constructor\");\n",
    "    }\n",
    "}\n",
    "\n",
    "public class Test extends Base{\n",
    " \n",
    "    static{\n",
    "        System.out.println(\"test static\");\n",
    "    }\n",
    "     \n",
    "    public Test(){\n",
    "        System.out.println(\"test constructor\");\n",
    "    }\n",
    "}\n",
    "\n",
    "new Test();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　至于为什么是这个结果，我们先不讨论，先来想一下这段代码具体的执行过程，在执行开始，先要寻找到main方法，因为main方法是程序的入口，但是在执行main方法之前，必须先加载Test类，而在加载Test类的时候发现Test类继承自Base类，因此会转去先加载Base类，在加载Base类的时候，发现有static块，便执行了static块。在Base类加载完成之后，便继续加载Test类，然后发现Test类中也有static块，便执行static块。在加载完所需的类之后，便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器，然后再调用自身的构造器。因此，便出现了上面的输出结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 这段代码的输出结果是什么？(!debug)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30mpublic class Test {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    Person person = new Person(\"Test\");\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    static{\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        System.out.println(\"test static\");\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m     \u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public Test() {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        System.out.println(\"test constructor\");\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m     \u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    public static void main(String[] args) {\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m        new MyClass();\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    }\u001b[0m",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m}\u001b[0m",
      "\u001b[1m\u001b[31mUnresolved dependencies:\u001b[0m",
      "\u001b[1m\u001b[31m   - class Person\u001b[0m",
      "\u001b[1m\u001b[31m   - class MyClass\u001b[0m"
     ]
    }
   ],
   "source": [
    "public class Test {\n",
    "    Person person = new Person(\"Test\");\n",
    "    static{\n",
    "        System.out.println(\"test static\");\n",
    "    }\n",
    "     \n",
    "    public Test() {\n",
    "        System.out.println(\"test constructor\");\n",
    "    }\n",
    "     \n",
    "    public static void main(String[] args) {\n",
    "        new MyClass();\n",
    "    }\n",
    "}\n",
    " \n",
    "class Person{\n",
    "    static{\n",
    "        System.out.println(\"person static\");\n",
    "    }\n",
    "    public Person(String str) {\n",
    "        System.out.println(\"person \"+str);\n",
    "    }\n",
    "}\n",
    " \n",
    " \n",
    "class MyClass extends Test {\n",
    "    Person person = new Person(\"MyClass\");\n",
    "    static{\n",
    "        System.out.println(\"myclass static\");\n",
    "    }\n",
    "     \n",
    "    public MyClass() {\n",
    "        System.out.println(\"myclass constructor\");\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "test static\n",
    "myclass static\n",
    "person static\n",
    "person Test\n",
    "test constructor\n",
    "person MyClass\n",
    "myclass constructor\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　类似地，我们还是来想一下这段代码的具体执行过程。首先加载Test类，因此会执行Test类中的static块。接着执行new MyClass()，而MyClass类还没有被加载，因此需要加载MyClass类。在加载MyClass类的时候，发现MyClass类继承自Test类，但是由于Test类已经被加载了，所以只需要加载MyClass类，那么就会执行MyClass类的中的static块。在加载完之后，就通过构造器来生成对象。而在生成对象的时候，必须先初始化父类的成员变量，因此会执行Test中的Person person = new Person()，而Person类还没有被加载过，因此会先加载Person类并执行Person类中的static块，接着执行父类的构造器，完成了父类的初始化，然后就来初始化自身了，因此会接着执行MyClass中的Person person = new Person()，最后执行MyClass的构造器。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 这段代码的输出结果是什么？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "ename": "CompilationException",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    static System.out.println\u001b[0m\u001b[1m\u001b[30m\u001b[41m\u001b[0m\u001b[1m\u001b[30m(\"test static 2\");\u001b[0m",
      "\u001b[1m\u001b[31m<identifier> expected\u001b[0m",
      "",
      "\u001b[1m\u001b[30m|   \u001b[1m\u001b[30m    static System.out.println(\u001b[0m\u001b[1m\u001b[30m\u001b[41m\u001b[0m\u001b[1m\u001b[30m\"test static 2\");\u001b[0m",
      "\u001b[1m\u001b[31millegal start of type\u001b[0m",
      ""
     ]
    }
   ],
   "source": [
    "public class Test{\n",
    "    static{\n",
    "        System.out.println(\"test static 1\");\n",
    "    }\n",
    "    \n",
    "    public static void main(String[] args) {\n",
    "         \n",
    "    }\n",
    "    \n",
    "    static System.out.println(\"test static 2\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "test static 1\n",
    "test static 2\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　虽然在main方法中没有任何语句，但是还是会输出，原因上面已经讲述过了。另外，static块可以出现类中的任何地方（只要不是方法内部，记住，任何方法内部都不行），并且执行是按照static块的顺序执行的。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "11.0.8+10-post-Ubuntu-0ubuntu120.04"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "165px"
   },
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
